/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "PluginUtilities.h"

#include <fstream>
#include <boost/uuid/uuid.hpp>            // uuid class
#include <boost/uuid/uuid_generators.hpp> // generators
#include <boost/uuid/uuid_io.hpp>         // streaming operators etc.
#include <boost/algorithm/string/replace.hpp>
#include "constant/PluginConstants.h"
#include "system/System.hpp"
#include "common/CTime.h"
#include "common/EnvVarManager.h"
#include "io_device/FileSystemIO.hpp"
#include "common/Path.h"
#include "ConfigIniReaderImpl.h"
#include "device_access/ControlDevice.h"
#include "device_access/dorado/DoradoBlock.h"
#include "security/cmd/CmdParam.h"
#include "security/cmd/CmdExecutor.h"
#include "NfsContextWrapper.h"
#include <boost/asio.hpp>

using namespace std;
using namespace Module;
using namespace std::literals::chrono_literals;
using namespace Module;

namespace {
    constexpr int TIME_STR_LEN = 80;
    constexpr auto MODULE = "Utilities";

    constexpr uint16_t MP_FAILED  = Module::FAILED;
    constexpr uint16_t MP_SUCCESS  = Module::SUCCESS;
    constexpr uint16_t MAX_CREATE_DIR_RETRY_CNT = 3;
    constexpr uint16_t MAX_REMOVE_DIR_RETRY_CNT = 3;
    constexpr uint16_t DEFAULT_REQUEST_RETRY_TIMER = 3000;
    const std::string CFGFILE = "/opt/DataBackup/ProtectClient/ProtectClient-E/conf/testcfg.tmp";
    const std::string DME_NAS_SCAN_CTRL_MAX_DATASIZE = "10737418240";
    const uint32_t DME_NAS_SCAN_CTRL_MAX_ENTRIES = 100000;
    const uint64_t DME_NAS_SCAN_CTRL_MAX_DATASIZE_UINT64 = 10737418240;
}

namespace PluginUtils {
    // 输入是ip列表，以逗号分割。检查通过管理网络访问8088端口是否连通

void LogCmdExecuteError(int retCode, const std::vector<std::string> &output, const std::vector<std::string> &errOutput)
{
    string stdoutMsg;
    for (auto &it : output) {
        stdoutMsg += it + " ";
    }
    string stderrMsg;
    for (const auto &it : errOutput) {
        stderrMsg += it + " ";
    }
    HCP_Log(DEBUG, MODULE) << "run shell failed with code:" << retCode
        << ", stdout: " << stdoutMsg
        << ", stderr: " << stderrMsg << HCPENDLOG;
}

bool CheckDeviceNetworkConnect(const std::string &managerIps)
{
    if (managerIps.empty()) {
        ERRLOG("empty IP!");
        return false;
    }
    vector<string> output;
    vector<string> errOutput;
    vector<string> paramList { Module::CPath::GetInstance().GetRootPath(), managerIps };
    INFOLOG("CheckDeviceNetworkConnect managerIps: %s", managerIps.c_str());
    string cmd = "sudo sh ?/bin/checkIp.sh '?'";
    int ret = Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, paramList, output, errOutput);
    if (ret != MP_SUCCESS) {
        LogCmdExecuteError(ret, output, errOutput);
        return false;
    }
    return true;
}

std::string FormatTimeToStrBySetting(time_t timeInSeconds, const std::string& timeFormat)
{
    struct tm *tmp = localtime(&timeInSeconds);
    char time[TIME_STR_LEN];
    strftime(time, sizeof(time), timeFormat.c_str(), tmp);
    string timeStr(time);
    return timeStr;
}

std::string FormatTimeToStr(time_t timeInSeconds)
{
    return FormatTimeToStrBySetting(timeInSeconds, "%Y-%m-%d-%H:%M:%S");
}

int KinitTGTInner(std::string userName, std::string password, std::string krb5Keytab,
    const std::string& krb5Conf, const std::string& uniqueFileJoinField)
{
    /* set krb5 environment variable for krb5.conf */
    string agentHomePath = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    /* krb5 credentials cache file path */
    string krb5CcnameFile = agentHomePath + KRB5CCNAMEPREFIX + uniqueFileJoinField;
    string krb5ConfigFile = agentHomePath + KRB5PREFIX + uniqueFileJoinField + KRB5CONFIGPOSTFIX;
    ofstream ofsConf(krb5ConfigFile, ios_base::out | ios_base::trunc | ios_base::binary);
    string krb5Config = Base64Decode(krb5Conf);
    ofsConf << krb5Config;
    ofsConf.close();

    /* set krb5 password or keytab */
    string krb5KeytabFile;
    if (!krb5Keytab.empty()) {
        krb5KeytabFile = agentHomePath + KRB5PREFIX +  uniqueFileJoinField + KRB5KEYTABPOSTFIX;
        string keytab = Base64Decode(krb5Keytab);
        ofstream ofsKeytab(krb5KeytabFile, ios_base::out | ios_base::trunc | ios_base::binary);
        ofsKeytab << keytab;
        ofsKeytab.close();
    }
    // using the security krb5
    string cmd;
    string arg1;
    string arg2;
    if (!password.empty()) {
        arg1 = password;
        arg2 = Module::CPath::GetInstance().GetRootPath();
        cmd = "echo '?' | sudo ?/bin/krb5KinitTGT.sh psd '?' '?' '?'";
    } else {
        arg1 = Module::CPath::GetInstance().GetRootPath();
        arg2 = krb5KeytabFile;
        cmd = "sudo ?/bin/krb5KinitTGT.sh ktb '?' '?' '?' '?'";
    }
    vector<string> paramList = { arg1, arg2, userName, krb5ConfigFile, krb5CcnameFile };
    INFOLOG("KinitTGT param, cmd: %s, userName: %s, keytabFile: %s, configFile: %s, ccnameFile: %s",
        cmd.c_str(), userName.c_str(), krb5KeytabFile.c_str(), krb5ConfigFile.c_str(), krb5CcnameFile.c_str());
    vector<string> output;
    vector<string> errput;
    if (Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, paramList, output, errput) != MP_SUCCESS) {
        std::for_each(output.begin(), output.end(), [&] (const std::string& v) { ERRLOG("output: %s", v.c_str());});
        std::for_each(errput.begin(), errput.end(), [&] (const std::string& v) { ERRLOG("errput: %s", v.c_str());});
        return MP_FAILED;
    }
    HCP_Log(INFO, MODULE) << "kinit Success for: " << Module::WipeSensitiveDataForLog(userName) << HCPENDLOG;
    return MP_SUCCESS;
}

std::string Base64Encode(const std::string &in)
{
    string out;
    // Characters involved in base64 encoding and decoding
    const string base64Array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    unsigned int val = NUMBER0;
    int valb = -NUMBER6;
    for (unsigned char c : in) {
        val = (val << NUMBER8) + c;
        valb += NUMBER8;
        while (valb >= NUMBER0) {
            out.push_back(base64Array[(val >> valb) & 0x3F]);
            valb -= NUMBER6;
        }
    }
    if (valb > -NUMBER6) {
        out.push_back(base64Array[((val << NUMBER8) >> (valb + NUMBER8)) & 0x3F]);
    }
    while (out.size() % NUMBER4 != 0) {
        out.push_back('=');
    }
    return out;
}

std::string Base64Decode(const std::string &in)
{
    string out;
    vector<int> T(NUMBER256, -1);
    // Characters involved in base64 encoding and decoding
    const string base64Array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    for (uint32_t i = 0; i < NUMBER64; i++) {
        T[base64Array[i]] = i;
    }
    int val = NUMBER0;
    int valb = -NUMBER8;
    for (unsigned char c : in) {
        if (T[c] == -1) {
            break;
        }
        val = (val <<NUMBER6) + T[c];
        valb += NUMBER6;
        if (valb >= NUMBER0) {
            out.push_back(char((val >> valb) & 0xFF));
            valb -= NUMBER8;
        }
    }
    return out;
}

size_t GenerateHash(std::string jobId)
{
    boost::hash<string> stringHash;
    return stringHash(jobId);
}

time_t GetCurrentTimeInSeconds()
{
    time_t currTime;
    Module::CTime::Now(currTime);
    return currTime;
}

std::string ConvertToReadableTime(time_t time)
{
    const uint32_t timeBufferSize = 80;
    char buffer[timeBufferSize];
    struct tm timeinfo {};
    localtime_r(&time, &timeinfo);
    strftime(buffer, sizeof(buffer), "%d%m%Y%H%M%S", &timeinfo);
    string timeStr(buffer);
    return timeStr;
}

time_t GetCurrentTimeInSeconds(std::string &dateAndTimeString)
{
    time_t currTime;
    Module::CTime::Now(currTime);
    dateAndTimeString = ConvertToReadableTime(currTime);
    return currTime;
}

bool CreateDirectory(const std::string& path)
{
    INFOLOG("CreateDirectory %s", path.c_str());
    if (path.empty()) {
        return true;
    }

    if (IsDirExist(path)) {
        DBGLOG("CreateDirectory success, dir exist: %s", path.c_str());
        return true;
    }

    std::string parentDir = GetPathName(path);
    if (!CreateDirectory(parentDir)) {
        return false;
    }

    int res = mkdir(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
    if (res != Module::SUCCESS && errno != EEXIST) {
        ERRLOG("CreateDirectory fail for: %s, errno: %u", path.c_str(), errno);
        return false;
    }
    return true;
}

bool RemoveDirectory(std::string path)
{
    bool res = false;
    int retry = 0;
    Module::FileSystemIO fsIO {};
    do {
        retry++;
        res = fsIO.RemoveAll(path.c_str());
        if (res) {
            break;
        }
        sleep(retry);
    } while (retry < MAX_REMOVE_DIR_RETRY_CNT);

    if (!res)
        HCP_Log(ERR, MODULE) << "Remove path fail for: " << WIPE_SENSITIVE(path) << HCPENDLOG;
    return res;
}

bool Remove(std::string path)
{
    bool flag = false;
    try {
        flag = boost::filesystem::exists(path);
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "path exists() exeption: " << WIPE_SENSITIVE(e.code().message())
        << ", path: " << path << HCPENDLOG;
        return false;
    }
    if (!flag) {
        return true;
    }

    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    bool res = false;
    int retry = 0;
    do {
        retry++;
        try {
            res = boost::filesystem::remove_all(path);
        } catch (const boost::filesystem::filesystem_error &e) {
            HCP_Log(ERR, MODULE) << "remove_all() exeption: " << WIPE_SENSITIVE(e.code().message())
            << ", path: " << path << HCPENDLOG;
        }
        if (res) {
            break;
        }
        sleep(retry);
    } while (retry < maxRemoveDirRetryCnt);

    if (!res)
        HCP_Log(ERR, MODULE) << "Remove path fail for: " << WIPE_SENSITIVE(path) << HCPENDLOG;
    return res;
}

bool IsPathExists(const std::string &path)
{
    bool flag = false;
    try {
        flag = boost::filesystem::exists(path);
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "path exists() exeption: " << WIPE_SENSITIVE(e.code().message())
        << ", path: " << path << HCPENDLOG;
        return false;
    }
    if (!flag) {
        DBGLOG("path: %s doesn't exist", path.c_str());
        return false;
    }
    return true;
}

bool Rename(std::string srcPath, std::string dstPath)
{
    if (!IsPathExists(srcPath)) {
        return false;
    }
    if (IsPathExists(dstPath)) {
        if (!Remove(dstPath)) {
            HCP_Log(ERR, MODULE) << "dst path: " << dstPath << " exist and remove failed" << HCPENDLOG;
            return false;
        }
    }

    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    bool res = false;
    int retry = 0;
    do {
        retry++;
        res = rename(srcPath.c_str(), dstPath.c_str());
        if (res) {
            DBGLOG("Rename src path: %s to dst path: %s success", srcPath.c_str(), dstPath.c_str());
            break;
        }
        sleep(retry);
    } while (retry < maxRemoveDirRetryCnt);
    if (!res) {
        ERRLOG("Rename src path: %s to dst path: %s failed", srcPath.c_str(), dstPath.c_str());
        return false;
    }
    return true;
}

std::string GetPathName(const std::string &filePath)
{
    char sep = '/';
    size_t fileoffset = filePath.rfind(sep, filePath.length());
    if (fileoffset != string::npos) {
        return (filePath.substr(0, fileoffset));
    }

    return ("");
}

std::string GetFileName(const std::string& filePath)
{
    char sep = '/';
    size_t fileoffset = filePath.rfind(sep, filePath.length());
    if (fileoffset == string::npos) {
        return filePath;
    }
    size_t fileNameLen = filePath.length() - fileoffset - 1;
    size_t fileStarPos = fileoffset + 1;
    return filePath.substr(fileStarPos, fileNameLen);
}

bool IsDirExist(const std::string& pathName)
{
    if (!IsPathExists(pathName)) {
        return false;
    }

    bool res = false;
    try {
        res = boost::filesystem::is_directory(pathName);
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "pathName is_directory() exeption: " << WIPE_SENSITIVE(e.code().message())
        << ", pathName: " << pathName << HCPENDLOG;
        return false;
    }
    return res;
}

bool IsFileExist(const std::string& fileName)
{
    if (!IsPathExists(fileName)) {
        return false;
    }

    bool res = false;
    try {
        res = boost::filesystem::is_regular_file(fileName);
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "fileName is_regular_file() exeption: " << WIPE_SENSITIVE(e.code().message())
        << ", fileName: " << fileName << HCPENDLOG;
        return false;
    }
    return res;
}

std::string FormatCapacity(uint64_t capacity)
{
    const uint64_t NUM_1024 = 1024;
    const uint64_t KB_size = NUM_1024;
    const uint64_t MB_size = NUM_1024 * KB_size;
    const uint64_t GB_size = NUM_1024 * MB_size;
    const uint64_t TB_size = NUM_1024 * GB_size;
    const uint64_t PB_size = NUM_1024 * TB_size;
    float formatCapacity;

    if ((capacity >= MB_size) && (capacity < GB_size)) {
        formatCapacity = static_cast<float>(capacity) / MB_size;
        return FloatToString(formatCapacity) + "MB";
    } else if ((capacity >= GB_size) && (capacity < TB_size)) {
        formatCapacity = static_cast<float>(capacity) / GB_size;
        return FloatToString(formatCapacity) + "GB";
    } else if ((capacity >= TB_size) && (capacity < PB_size)) {
        formatCapacity = static_cast<float>(capacity) / TB_size;
        return FloatToString(formatCapacity, NUMBER2) + "TB";
    } else if ((capacity >= KB_size) && (capacity < MB_size)) {
        formatCapacity = static_cast<float>(capacity) / KB_size;
        return FloatToString(formatCapacity) + "KB";
    } else if (capacity >= PB_size) {
        formatCapacity = static_cast<float>(capacity) / PB_size;
        return FloatToString(formatCapacity) + "PB";
    } else {
        return to_string(capacity) + "B";
    }
}

std::string FloatToString(const float &val, const uint8_t &precisson)
{
    stringstream sstream;
    sstream << setiosflags(ios::fixed) << setprecision(precisson) << val;
    return sstream.str();
}

std::string GetUniqueId()
{
    string uniqueId;
    try {
        boost::uuids::random_generator gen;
        boost::uuids::uuid guid = gen();
        uniqueId = boost::uuids::to_string(guid);
        boost::replace_all(uniqueId, "-", "");
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "GetUniqueId() exeption: " << WIPE_SENSITIVE(e.code().message()) << HCPENDLOG;
        return uniqueId;
    } catch (boost::uuids::entropy_error &e) {
        ERRLOG("GetUniqueId() exeption: entropy_error");
        return uniqueId;
    } catch (...) {
        ERRLOG("GetUniqueId ...");
        return uniqueId;
    }
    return uniqueId;
}

bool IsConnectIP(const std::string& ip)
{
    std::string cmd = "ping -c 1 -w 3 " + ip;  // 设置3秒超时
    vector<string> output;
    vector<string> errOutput;
    if (Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, {}, output, errOutput) != Module::SUCCESS) {
        ERRLOG("IP(%s) can't connected", ip.c_str());
        return false;
    }
    return true;
}

int CheckNfsAccessibility(const NasMountParams& param)
{
    std::string ipList {};
    for (uint32_t i = 0; i < param.nasServerIpList.size(); ++i) {
        ipList += param.nasServerIpList[i] + " ";
        if (!IsConnectIP(param.nasServerIpList[i])) {
            continue;
        }
        std::string rootPath = "nfs://" + param.nasServerIpList[i] + param.sharePath + "/";
        std::shared_ptr<NfsContextWrapper> rootNfs = make_shared<NfsContextWrapper>(rootPath, "");
        int ret = rootNfs->NfsMount(true);
        if (ret == MP_SUCCESS) {
            rootNfs->NfsDestroyContext();
            INFOLOG("Access success for url: %s", rootPath.c_str());
            return MP_SUCCESS;
        }
        ERRLOG("Mount for url: %s failed with error : %d", rootPath.c_str(), ret);
    }

    ERRLOG("Cannot access share path: %s, ip list: %s", param.sharePath.c_str(), ipList.c_str());
    return MP_FAILED;
}

// int MountNFS(NasMountParams& param, std::string &outSelectedSvcIp)
int MountNFS(const NasMountParams& param, std::string &outSelectedSvcIp)
{
    outSelectedSvcIp = "";
    for (uint32_t i = 0; i < param.nasServerIpList.size(); ++i) {
        std::string scriptName = Module::CPath::GetInstance().GetRootPath() + "/bin/checkMount.sh";
        std::vector<std::string> result;
        std::vector<Module::CmdParam> cmd {
            CmdParam(SCRIPT_CMD_NAME, scriptName),
            CmdParam(CMD_OPTION_PARAM, "mount"),
            CmdParam(COMMON_PARAM, param.nasServerIpList[i]),
            CmdParam(COMMON_PARAM, param.protocol),
            CmdParam(COMMON_PARAM, param.version),
            CmdParam(COMMON_PARAM, Base64Encode(param.sharePath)),
            CmdParam(COMMON_PARAM, param.localMountPath),
            CmdParam(COMMON_PARAM, param.mountOptions),
        };
        int ret = Module::RunCommand(scriptName, cmd, result);
        if (ret != MP_SUCCESS) {
            HCP_Log(WARN, MODULE) << "Mount error for svcIP: " << param.nasServerIpList[i] << HCPENDLOG;
            continue;
        }

        outSelectedSvcIp = param.nasServerIpList[i];
        HCP_Log(INFO, MODULE) << "Mount " << WIPE_SENSITIVE(param.sharePath)
                              << " to " << WIPE_SENSITIVE(param.localMountPath)
                              << " using proto=" << WIPE_SENSITIVE(param.protocol)
                              << ", vers=" << WIPE_SENSITIVE(param.version)
                              << " successful" << HCPENDLOG;

        return MP_SUCCESS;
    }

    HCP_Log(ERR, MODULE) << "Mount " << outSelectedSvcIp << "://" << WIPE_SENSITIVE(param.sharePath) << "/"
                         << " to " << WIPE_SENSITIVE(param.localMountPath)
                         << " using proto=" << WIPE_SENSITIVE(param.protocol)
                         << ", vers=" << WIPE_SENSITIVE(param.version)
                         << " failed" << HCPENDLOG;
    return MP_FAILED;
}

int UnmountNas(std::string localPath)
{
    int ret = 0;
    HCP_Log(INFO, MODULE) << "Begin to umount " << WIPE_SENSITIVE(localPath) << HCPENDLOG;
    std::string scriptName = Module::CPath::GetInstance().GetRootPath() + "/bin/checkMount.sh";
    std::vector<std::string> result;
    std::vector<Module::CmdParam> cmd {
        CmdParam(SCRIPT_CMD_NAME, scriptName),
        CmdParam(CMD_OPTION_PARAM, "umount"),
        CmdParam(COMMON_PARAM, localPath)
    };
    ret = Module::RunCommand(scriptName, cmd, result);
    if (ret != 0) {
        ERRLOG("exec cmd failed!");
        return MP_FAILED;
    }
    HCP_Log(INFO, MODULE) << "Unmount Success for: " << WIPE_SENSITIVE(localPath) << HCPENDLOG;
    return MP_SUCCESS;
}

bool PathExistMandate(const std::string &path)
{
    uint16_t maxExistRetryCnt = 3;
    bool res = false;
    int retry = 0;
    Module::FileSystemIO fsIO {};
    do {
        retry++;
        res = fsIO.Exists(path.c_str());
        if (res) {
            break;
        }
        sleep(retry);
    } while (retry < maxExistRetryCnt);

    return res;
}

bool GetFileListInDirectory(const std::string &path, std::vector<std::string> &fileList)
{
    uint16_t maxGetListRetryCnt = 3;
    bool res = false;
    int retry = 0;
    Module::FileSystemIO fsIO {};
    do {
        retry++;
        res = fsIO.GetFileListInDirectory(path.c_str(), fileList);
        if (res) {
            break;
        }
        fileList.clear();
        sleep(retry);
    } while (retry < maxGetListRetryCnt);

    return res;
}

bool GetInnerPodIp(std::string& podIp)
{
    std::string cmd = "cat " + CFGFILE + " | grep POD_IP | awk -F \"=\" \'{print $2}\'";
    DBGLOG("run cmd : %s", cmd.c_str());
    vector<string> output;
    vector<string> errput;
    int ret = Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, {}, output, errput);
    if (ret != 0 || output.empty()) {
        ERRLOG("get inner pod ip failed");
        for_each(output.begin(), output.end(), [&] (const string& v) { ERRLOG("output: %s", v.c_str());});
        for_each(errput.begin(), errput.end(), [&] (const string& v) { ERRLOG("errput: %s", v.c_str());});
        return false;
    }
    podIp = output[0];
    return true;
}

bool OperateIpsRule(const std::vector<std::string>& ipList, const std::string& operation)
{
    // 校验是否是内置nas，如果不是内置nas则直接退出
    auto configReader = ConfigReaderImpl::instance();
    std::string backupScene = configReader->GetBackupSceneFromXml("backup_scene");
    std::string deployType = GetDeployType();
    DBGLOG("only X series need to add route, deploytype: %s", deployType.c_str());
    if (!(deployType == X3000_DEPLOY_TYPE || deployType == X6000_DEPLOY_TYPE ||
        deployType == X8000_DEPLOY_TYPE || deployType == X9000_DEPLOY_TYPE)) {
        return true;
    }

    // 外置代理，不设置路由
    if (backupScene == "0" || ipList.size() == 0) {
        return true;
    }
    std::string podIp;
    if (!GetInnerPodIp(podIp)) {
        ERRLOG("get pod ip failed");
        return false;
    }
    Module::ControlDeviceInfo deviceInfo;
    deviceInfo.enableProxy = false;
    auto deviceInstance = std::make_unique<DoradoBlock>(deviceInfo, false);
    IpRuleOperation oper;
    if (operation == "ADD") {
        oper = IpRuleOperation::ADD;
    } else if (operation == "DELETE") {
        oper = IpRuleOperation::DELETE;
    }
    for (const std::string& ip : ipList) {
        if (ip.size() < NUMBER7) {
            WARNLOG("%s is  not a effctive ip", ip.c_str());
            continue;
        }
        int errcode;
        DBGLOG("enter %s ip rule for ip : %s", operation.c_str(), ip.c_str());
        if (!deviceInstance->OperateIpRule(ip, podIp, oper, errcode)) {
            ERRLOG("%s ip route for ip : %s failed, errno : %d", operation.c_str(), ip.c_str(), errcode);
            return false;
        }
    }
    return true;
}

bool OperateIpsRuleForRemoteIp(const std::vector<std::string>& ipList, const std::string& operation,
                               std::string& availableIp)
{
    if (ipList.empty()) {
        //127.0.0.1时，不需要添加路由
        return true;
    }
    if (operation == "DELETE") {
        return OperateIpsRule({availableIp}, operation);
    }
    for (const std::string& ip : ipList) {
        if (OperateIpsRule({ip}, operation)) {
            availableIp = ip;
            return true;
        }
    }
    return false;
}

bool IsInnerAgent()
{
    auto configReader = ConfigReaderImpl::instance();
    std::string backupScene = configReader->GetBackupSceneFromXml("backup_scene");
    // 外置代理
    if (backupScene == "0") {
        return false;
    }
    return true;
}

bool WriteFile(const std::string &filePath, std::vector<std::string>& fileContent)
{
    std::ofstream writeFd {};
    for (int retryCnt = 0; retryCnt < NUMBER3; retryCnt++) {
        try {
            writeFd.open(filePath.c_str(), std::ios::out | std::ios::trunc);
            if (writeFd.is_open()) {
                break;
            }
            writeFd.close();
            sleep(NUMBER1);
        } catch (std::exception &e) {
            ERRLOG("Write %s failed, ERR: %s", filePath.c_str(), e.what());
        }
    }

    if (!writeFd.is_open()) {
        char errmsg[NUMBER256];
        strerror_r(errno, errmsg, NUMBER256);
        HCP_Log(ERR, MODULE) << "Write " << WIPE_SENSITIVE(filePath) << " failed, ERR: " << errmsg << HCPENDLOG;
        return false;
    }

    for (std::string& line : fileContent) {
        writeFd << line;
    }
    writeFd.close();
    return true;
}

bool ReadFile(const std::string &filePath, std::vector<std::string>& fileContent)
{
    std::ifstream readFd {};
    for (int retryCnt = 0; retryCnt < NUMBER3; retryCnt++) {
        try {
            readFd.open(filePath.c_str(), std::ios::in);
            if (readFd.is_open()) {
                break;
            }
            readFd.close();
            sleep(NUMBER1);
        } catch (std::exception &e) {
            ERRLOG("Read %s failed, ERR: %s", filePath.c_str(), e.what());
        }
    }
    if (!readFd.is_open()) {
        char errmsg[NUMBER256];
        strerror_r(errno, errmsg, NUMBER256);
        ERRLOG("Read %s failed, ERR: %s", filePath.c_str(), errmsg);
        return false;
    }
    stringstream jsonFileContent {};
    jsonFileContent << readFd.rdbuf();
    readFd.close();

    std::string currentLine {};
    while (getline(jsonFileContent, currentLine)) {
        fileContent.emplace_back(currentLine);
    }
    return true;
}

bool RemoveFile(const std::string &path)
{
    uint16_t maxRemoveRetry = 3;
    bool res = false;
    int retry = 0;
    Module::FileSystemIO fsIO {};
    do {
        retry++;
        res = fsIO.Remove(path.c_str());
        if (res) {
            break;
        }
        sleep(retry);
    } while (retry < maxRemoveRetry);

    return res;
}

bool CopyFile(const std::string &srcPath, const std::string &destDir)
{
    uint16_t maxCopyRetryCnt = 3;
    bool res = false;
    int retry = 0;
    Module::FileSystemIO fsIO {};
    do {
        retry++;
        res = fsIO.Copy(srcPath.c_str(), destDir.c_str());
        if (res) {
            break;
        }
        sleep(retry);
    } while (retry < maxCopyRetryCnt);

    return res;
}

bool RemoveDir(const std::string &dirName)
{
    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    bool res = false;
    int retry = 0;
    Module::FileSystemIO fsIO {};
    do {
        retry++;
        res = fsIO.RemoveAll(dirName.c_str());
        if (res) {
            break;
        }
        sleep(retry);
    } while (retry < maxRemoveDirRetryCnt);

    return res;
}

bool RenameDir(const std::string &oldName, const std::string &newName)
{
    bool res = false;

    /* Remove destination dir if exists */
    res = RemoveDir(newName);
    if (!res) {
        HCP_Log(ERR, MODULE) << "Remove dir failed: " <<  WIPE_SENSITIVE(newName) << HCPENDLOG;
        return false;
    }

    /* Now rename to destination dor */
    res = Rename(oldName, newName);
    return res;
}

std::string FormatSpeed(uint64_t speed)
{
    const uint64_t KB_size = NUMBER1024;
    const uint64_t MB_size = NUMBER1024 * KB_size;
    const uint64_t GB_size = NUMBER1024 * MB_size;
    const uint64_t TB_size = NUMBER1024 * GB_size;
    const uint64_t PB_size = NUMBER1024 * TB_size;
    float formatSpeed;

    if ((speed >= MB_size) && (speed < GB_size)) {
        formatSpeed = static_cast<float>(speed) / MB_size;
        return FloatToString(formatSpeed) + "MBps";
    } else if ((speed >= GB_size) && (speed < TB_size)) {
        formatSpeed = static_cast<float>(speed) / GB_size;
        return FloatToString(formatSpeed) + "GBps";
    } else if ((speed >= TB_size) && (speed < PB_size)) {
        formatSpeed = static_cast<float>(speed) / TB_size;
        return FloatToString(formatSpeed) + "TBps";
    } else if ((speed >= KB_size) && (speed < MB_size)) {
        formatSpeed = static_cast<float>(speed) / KB_size;
        return FloatToString(formatSpeed) + "KBps";
    } else if (speed >= PB_size) {
        formatSpeed = static_cast<float>(speed) / PB_size;
        return FloatToString(formatSpeed) + "PBps";
    } else {
        return std::to_string(speed) + "Bytes/s";
    }
}

std::string StripSqrBracketsFromIpAddress(const std::string &ipAddress)
{
    std::string ipAddressTemp {};
    ipAddressTemp = ipAddress;

    if (ipAddressTemp[0] == '[' && ipAddressTemp[ipAddressTemp.length() - 1] == ']') {
        /* Erase '[' in the beginning */
        ipAddressTemp.erase(ipAddressTemp.begin());

        /* Erase ']' in the end */
        ipAddressTemp.erase(ipAddressTemp.begin() + ipAddressTemp.length() - 1, ipAddressTemp.end());
    }

    return ipAddressTemp;
}

bool RecurseCreateDirectory(const std::string& path)
{
    DBGLOG("create recur dir: %s", path.c_str());
    std::vector<std::string> splitDir;
    std::stringstream iss(path);
    std::string dir;
    while (std::getline(iss, dir, '/')) {
        if (dir.empty()) {
            continue;
        }
        splitDir.push_back(dir);
    }
    for (std::size_t i = 0; i < splitDir.size(); i++) {
        std::string path = "/";
        for (std::size_t j = 0; j <= i; j++) {
            path += splitDir[j] + "/";
        }
        int ret = mkdir(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
        int err = errno;
        if (ret != 0 && err != EEXIST) {
            DBGLOG("create dir failed: %s, err: %d", path.c_str(), err);
            return false;
        }
    }
    return true;
}

std::string GetDeployType()
{
    string cmd = RDADMIN_BASH + "\"" + Module::EnvVarManager::GetInstance()->GetAgentHomePath() + XML_READ_TYPE + "\"";
    vector<string> output;
    vector<string> errOutput;
    HCP_Log(INFO, MODULE) << "run cmd : " << cmd << HCPENDLOG;
    Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, { }, output, errOutput);
    if (output.empty()) {
        WARNLOG("Get deploy type failed!");
        return "";
    }
    return output[0];
}

std::string StandardPath(const std::string& path)
{
    string temp = path;
    for (auto it = temp.begin(); it + 1 != temp.end();) {
        if (*it == Module::PATH_SEPARATOR[0] && *(it + 1) == Module::PATH_SEPARATOR[0]) {
            temp.erase(it + 1);
        } else {
            ++it;
        }
    }
    return temp;
}

bool IsPigzExist()
{
    vector<std::string> output;
    std::vector<Module::CmdParam> cmd {
        CmdParam(COMMON_CMD_NAME, "pigz"),
        CmdParam(CMD_OPTION_PARAM, "--version")
    };
    int ret = Module::RunCommand("pigz", cmd, output);
    if (ret != 0) {
        ERRLOG("pigz --version cmd failed! %d", ret);
        for (auto msg : output) {
            ERRLOG("errmsg : %s", msg.c_str());
        }
    }
    return (ret == 0);
}

int CheckAndUnmount(std::string mountPath)
{
    vector<std::string> output;
    std::vector<Module::CmdParam> cmd {
        CmdParam(COMMON_CMD_NAME, "mountpoint"),
        CmdParam(PATH_PARAM, mountPath)
    };
    int ret = Module::RunCommand("mountpoint", cmd, output);
    if (ret == 0) {
        WARNLOG("GenerateSubJob umount nas failed, try again");
        return UnmountNas(mountPath);
    }
    INFOLOG("GenerateSubJob umount nas success");
    return MP_SUCCESS;
}

void ResolveDomain(const std::string& domain, std::vector<std::string>& ipList)
{
    if (domain == "127.0.0.1") {
        //回环地址直接跳过解析
        return;
    }
    try {
        boost::asio::io_context io_context;                     // 创建 io_context（Boost 1.66 及以上）
        boost::asio::ip::tcp::resolver resolver(io_context);    //  创建域名解析器
        auto results = resolver.resolve(domain, "");            // 解析域名（可指定端口，如 "http" 或 "80"）
        if (!results.empty()) {
            for (const auto& entry : results) {
                ipList.push_back(entry.endpoint().address().to_string());
            }
        } else {
            ERRLOG("No IP address found for domain: %s", domain.c_str());
            ipList.push_back(domain);
        }
    } catch (const boost::system::system_error& e) {
        ERRLOG("Error resolving domain: %s", e.what());
    }
    return;
}

std::string ConvertGBToBytes(const std::string& gbStr)
{
    try {
        uint64_t gbValue = std::stoull(gbStr);  // string 转无符号整数
        uint64_t bytes = gbValue * 1024uLL * 1024uLL * 1024uLL;
        std::string bytesStr = std::to_string(bytes);  // 转为 string
        return bytesStr;
    } catch (const std::exception& e) {
        ERRLOG("convert GB to bytes failed, err: %s", e.what());
        return DME_NAS_SCAN_CTRL_MAX_DATASIZE;
    } catch (...) {
        ERRLOG("convert GB to bytes failed, unknown exception");
        return DME_NAS_SCAN_CTRL_MAX_DATASIZE;
    }
}

uint32_t SafeStou32(const std::string& str)
{
    try {
        size_t pos = 0;
        unsigned long res = std::stoul(str, &pos, 10);
        if (pos != str.size() || res > UINT32_MAX) {
            ERRLOG("Value out of uint32_t range or contains invalid characters,use default value");
            return DME_NAS_SCAN_CTRL_MAX_ENTRIES; // 这个函数只在填写扫描参数时用到，所以出错时返回配置文件里的默认值
        }
        return static_cast<uint32_t>(res);
    } catch (const std::exception& e) {
        ERRLOG("Invalid Argument for stoul: %s, ERR:%s, use default value", str.c_str(), e.what());
        return DME_NAS_SCAN_CTRL_MAX_ENTRIES;
    }
}

uint64_t SafeStou64(const std::string& str)
{
    try {
        size_t pos = 0;
        unsigned long long res = std::stoull(str, &pos, 10);
        if (pos != str.size() || res > UINT64_MAX) {
            ERRLOG("Value out of uint64_t range or contains invalid characters, use default value");
            return DME_NAS_SCAN_CTRL_MAX_DATASIZE_UINT64;
        }
        return static_cast<uint64_t>(res);
    } catch (const std::exception& e) {
        ERRLOG("Invalid Argument for stoull: %s, ERR: %s, use default value", str.c_str(), e.what());
        return DME_NAS_SCAN_CTRL_MAX_DATASIZE_UINT64;
    }
}
} // namespace
